package com.itheima.stock.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.itheima.stock.config.StockInfoConfig;
import com.itheima.stock.constant.ParseType;
import com.itheima.stock.mapper.*;
import com.itheima.stock.pojo.entity.StockBlockRtInfo;
import com.itheima.stock.pojo.entity.StockMarketIndexInfo;
import com.itheima.stock.pojo.entity.StockOuterMarketIndexInfo;
import com.itheima.stock.pojo.entity.StockRtInfo;
import com.itheima.stock.service.IStockMarketIndexInfoService;
import com.itheima.stock.service.StockTimerTaskService;
import com.itheima.stock.utils.DateTimeUtil;
import com.itheima.stock.utils.IdWorker;
import com.itheima.stock.utils.ParserStockInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.message.StringFormattedMessage;
import org.joda.time.DateTime;


import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {
    @Resource
    private StockInfoConfig stockInfoConfig;
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private IdWorker idWorker;
    @Resource
    private IStockMarketIndexInfoService stockMarketIndexInfoService;
    @Resource
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Resource
    private ParserStockInfoUtil parserStockInfoUtil;
    @Resource
    private StockRtInfoMapper stockRtInfoMapper;
    @Resource
    private StockBusinessMapper stockBusinessMapper;
    private HttpEntity<Object> httpEntity;
    @Resource
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;
    @Resource
    RabbitTemplate rabbitTemplate;
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    private StockOuterMarketIndexInfoMapper stockOuterMarketIndexInfoMapper;


    /**
     * 获取A股大盘信息
     */
    @Override
    public void getInnerMarketInfo() {
        //1.构造新浪财经请求URL
        String url = stockInfoConfig.getMarketUrl() + String.join(",", stockInfoConfig.getInner());
        //1.1设置请求头

        /*HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Referer", "https://finance.sina.com.cn/realstock/company/sh000001/nc.shtml");
        httpHeaders.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0");
        //设置请求体
        HttpEntity<Object> httpEntity = new HttpEntity<>(httpHeaders);*/

        //2.发送请求获取国内大盘信息
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        //获取响应数据
        String body = responseEntity.getBody();
        //获取响应状态码
        int statusCodeValue = responseEntity.getStatusCodeValue();
        if (statusCodeValue != 200 || StringUtils.isEmpty(body)) {
            log.error("A股大盘信息获取失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
            //给运维钉钉，邮箱发出告警
            return;
        }
        //3.解析数据
        List stockMarketIndexInfos = parserStockInfoUtil.parser4StockOrMarketInfo(body, ParseType.INNER);

        /*//正则表达式编写
        String regex = "var hq_str_(.{8})=\"(.*)\";";
        //制定规则
        Pattern pattern = Pattern.compile(regex);
        //匹配数据
        Matcher matcher = pattern.matcher(body);
        List<StockMarketIndexInfo> stockMarketIndexInfos = new ArrayList<>();
        //循环匹配
        while (matcher.find()) {
            String marketCode = matcher.group(1);
            String marketInfo = matcher.group(2);
            String[] split = marketInfo.split(",");

            //封装数据
            //主键字段生成
            long id = idWorker.nextId();
            //指数名称
            String marketName = split[0];
            //开盘点
            BigDecimal openPoint = new BigDecimal(split[1]);
            //前收盘点
            BigDecimal preClosePoint = new BigDecimal(split[2]);
            //当前点
            BigDecimal curPoint = new BigDecimal(split[3]);
            //最高点
            BigDecimal maxPoint = new BigDecimal(split[4]);
            //最低点
            BigDecimal minPoint = new BigDecimal(split[5]);
            //成交量
            Long tradeAmount = Long.valueOf(split[8]);
            //成交额
            BigDecimal tradeVolume = new BigDecimal(split[9]);
            //当前时间
            Date curTime = DateTimeUtil.getDateTimeWithoutSecond(split[30] + " " + split[31]).toDate();

            //打印以上信息
            log.info("指数名称：{},开盘点：{},前收盘点：{},当前点：{},最高点：{},最低点：{},成交量：{},成交额：{},当前时间：{}", marketName, openPoint, preClosePoint, curPoint, maxPoint, minPoint, tradeAmount, tradeVolume, curTime);

            //4.数据封装
            StockMarketIndexInfo stockMarketIndexInfo = StockMarketIndexInfo.builder()
                    .id(id)
                    .marketName(marketName)
                    .openPoint(openPoint)
                    .preClosePoint(preClosePoint)
                    .curPoint(curPoint)
                    .maxPoint(maxPoint)
                    .minPoint(minPoint)
                    .tradeAmount(tradeAmount)
                    .tradeVolume(tradeVolume)
                    .curTime(curTime)
                    .marketCode(marketCode)
                    .build();

            //添加到集合中
            stockMarketIndexInfos.add(stockMarketIndexInfo);
        }*/
        log.info("A股大盘信息：{}", stockMarketIndexInfos);
        //5.向数据库中插入数据
        boolean b = stockMarketIndexInfoService.saveBatch(stockMarketIndexInfos);
        if (b) {
            //发送消息队列
            //参数1：交换机名称  参数2：路由键  参数3：消息
            rabbitTemplate.convertAndSend("stockExchange","inner.market",new Date());
            log.info("A股大盘信息插入成功 : {}，当前时间为：{}", stockMarketIndexInfos, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
        } else {
            log.error("A股大盘信息插入失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
        }
    }

    /**
     * 获取国内个股数据实时信息（分钟级）
     */
    @Override
    public void getStockRtIndex() {
        //1.构造新浪财经请求URL
        String marketUrl = stockInfoConfig.getMarketUrl();
        //获取所有股票代码集合
        List<String> codes = stockBusinessMapper.selectCodeList();
        //处理股票代码加上前缀
        List<String> codesList = codes.stream().map(code -> {
            code = code.startsWith("60") ? "sh" + code : "sz" + code;
            return code;
        }).toList();
        //将这些数据分割成各个部分 40 ： 15 15 10 使用guava工具包
        List<List<String>> partition = Lists.partition(codesList, 15);
        long startTime = System.currentTimeMillis();

        partition.forEach(list -> {
//            //原始方案
//            String url = marketUrl + String.join(",", list);
//
//         /*
//         HttpHeaders httpHeaders = new HttpHeaders();
//         httpHeaders.add("Referer", "https://finance.sina.com.cn/realstock/company/sh000001/nc.shtml");
//         httpHeaders.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0");
//         //设置请求体
//         HttpEntity<Object> httpEntity = new HttpEntity<>(httpHeaders);
//         */
//
//            //2.发送请求获取国内大盘信息
//            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
//            //获取响应数据
//            String body = responseEntity.getBody();
//            //获取响应状态码
//            int statusCodeValue = responseEntity.getStatusCodeValue();
//            if (statusCodeValue != 200 || StringUtils.isEmpty(body)) {
//                log.error("A股大盘信息获取失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
//                //给运维钉钉，邮箱发出告警
//                return;
//            }
//            //3.解析数据
//            List stockRtInfos = parserStockInfoUtil.parser4StockOrMarketInfo(body, ParseType.ASHARE);
//            log.info("国内个股信息：{}", stockRtInfos);
//            //todo 保存数据
//            boolean b = stockRtInfoMapper.saveBatch(stockRtInfos);
//            if (b) {
//                log.info("A股个股信息插入成功 : {}，当前时间为：{}", stockRtInfos, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
//            } else {
//                log.error("A股个股信息插入失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
//            }
//            //方案一：原始方案采集个股数据时将集合分片
//            new Thread(()->{
//                String url = marketUrl + String.join(",", list);
//
//                //2.发送请求获取国内大盘信息
//                ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
//                //获取响应数据
//                String body = responseEntity.getBody();
//                //获取响应状态码
//                int statusCodeValue = responseEntity.getStatusCodeValue();
//                if (statusCodeValue != 200 || StringUtils.isEmpty(body)) {
//                    log.error("A股大盘信息获取失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
//                    //给运维钉钉，邮箱发出告警
//                    return;
//                }
//                //3.解析数据
//                List stockRtInfos = parserStockInfoUtil.parser4StockOrMarketInfo(body, ParseType.ASHARE);
//                log.info("国内个股信息：{}", stockRtInfos);
//                //todo 保存数据
//                boolean b = stockRtInfoMapper.saveBatch(stockRtInfos);
//                if (b) {
//                    log.info("A股个股信息插入成功 : {}，当前时间为：{}", stockRtInfos, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
//                } else {
//                    log.error("A股个股信息插入失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
//                }
//            });
            //方案二：引入线程池
            threadPoolTaskExecutor.execute(()->{
                String url = marketUrl + String.join(",", list);

                //2.发送请求获取国内大盘信息
                ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
                //获取响应数据
                String body = responseEntity.getBody();
                //获取响应状态码
                int statusCodeValue = responseEntity.getStatusCodeValue();
                if (statusCodeValue != 200 || StringUtils.isEmpty(body)) {
                    log.error("A股大盘信息获取失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
                    //给运维钉钉，邮箱发出告警
                    return;
                }
                //3.解析数据
                List stockRtInfos = parserStockInfoUtil.parser4StockOrMarketInfo(body, ParseType.ASHARE);
                log.info("国内个股信息：{}", stockRtInfos);
                //todo 保存数据
                boolean b = stockRtInfoMapper.saveBatch(stockRtInfos);
                if (b) {
                    log.info("A股个股信息插入成功 : {}，当前时间为：{}", stockRtInfos, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
                } else {
                    log.error("A股个股信息插入失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
                }
            });
        });

        log.info("国内个股信息获取耗时：{}", System.currentTimeMillis() - startTime);
    }

    /**
     * 获取国内板块信息
     */
    @Override
    public void getStockBlockInfo() {
        //1.构造url
        String blockUrl = stockInfoConfig.getBlockUrl();
        //2.发送请求获取数据
        ResponseEntity<String> responseEntity = restTemplate.exchange(blockUrl, HttpMethod.GET, httpEntity, String.class);
        //获取响应数据
        String body = responseEntity.getBody();
        //获取响应状态码
        int statusCodeValue = responseEntity.getStatusCodeValue();
        if (statusCodeValue != 200 || StringUtils.isEmpty(body)) {
            log.error("板块信息获取失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
            return;
        }
        //3.解析数据
        //制定匹配规则
        String regex = "var S_Finance_bankuai_sinaindustry = (.+)";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(body);
        String group = null;
        //匹配
        if (matcher.find()) {
            group = matcher.group();
        }
        List<StockBlockRtInfo> stockBlockRtInfos = parserStockInfoUtil.parse4StockBlock(group);

        /*
        //将获取的数据转换成map集合
        Map blockMap = (Map) JSONObject.parse(group);
        //获取集合内的所有板块信息
        Collection values = blockMap.values();
        //4.板块信息封装
        List<StockBlockRtInfo> stockBlockRtInfos = (ArrayList<StockBlockRtInfo>) values.stream().map(value -> {
            String blockStr = (String) value;
            String[] split = blockStr.split(",");
            StockBlockRtInfo stockBlockRtInfo = StockBlockRtInfo.builder()
                    .id(idWorker.nextId())
                    .label(split[0])
                    .blockName(split[1])
                    .companyNum(Integer.parseInt(split[2]))
                    .avgPrice(new BigDecimal(split[3]))
                    .updownRate(new BigDecimal(split[5]))
                    .tradeAmount(Long.parseLong(split[6]))
                    .tradeVolume(new BigDecimal(split[7]))
                    .curTime(DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate())
                    .build();
            //返回
            return stockBlockRtInfo;
            //将数据收集为list集合
        }).collect(Collectors.toList());
        */

        //6.向数据库中插入数据
        boolean b = stockBlockRtInfoMapper.saveBatch(stockBlockRtInfos);
        if (b) {
            log.info("板块信息插入成功 : {}，当前时间为：{}", stockBlockRtInfos, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
        } else {
            log.error("板块信息插入失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
        }

    }

    /**
     * 获取外盘信息
     */
    @Override
    public void getOuterMarketInfo() {
        //1.构造url
        String outerUrl = stockInfoConfig.getOuterUrl();
        // 获取外盘参数
        List<String> outer = stockInfoConfig.getOuter();
        String url = outerUrl + String.join(",", outer);

        //2.发送请求获取数据
        //设置一系列请求头
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);

        //获取响应数据
        String body = responseEntity.getBody();
        int statusCodeValue = responseEntity.getStatusCodeValue();
        if (statusCodeValue != 200 || StringUtils.isEmpty(body)) {
            log.error("A股大盘信息获取失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
            //给运维钉钉，邮箱发出告警
            return;
        }
        //数据解析

        //3.数据封装
        //制定规则
        String regex = "var hq_str_(.+)=\"(.+)\"";
        Pattern pattern = Pattern.compile(regex);
        //匹配数据
        Matcher matcher = pattern.matcher(body);
        //4.封装数据
        ArrayList<Object> list = new ArrayList<>();
        while (matcher.find()) {
            String marketCode = matcher.group(1);
            String marketInfo = matcher.group(2);
            String[] split = marketInfo.split(",");
            StockOuterMarketIndexInfo stockOuterMarketIndexInfo = StockOuterMarketIndexInfo.builder()
                    .id(idWorker.nextId())
                    .marketCode(marketCode)
                    .marketName(split[0])
                    .curPoint(new BigDecimal(split[1]))
                    .updown(new BigDecimal(split[2]))
                    .rose(new BigDecimal(split[3]))
                    .curTime(DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate())
                    .build();
            list.add(stockOuterMarketIndexInfo);
        }
        //5.向数据库插入数据
        boolean b = stockOuterMarketIndexInfoMapper.saveBatch(list);
        if (b) {
            log.info("外盘信息插入成功 : {}，当前时间为：{}", list, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
        } else {
            log.error("外盘信息插入失败，当前时间为：{}", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(DateTime.now()));
        }

    }

    /**
     * bean生命周期的初始化回调方法
     */
    @PostConstruct
    //构造方法执行完之后执行
    public void initDate() {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Referer", "https://finance.sina.com.cn/realstock/company/sh000001/nc.shtml");
        httpHeaders.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0");
        //设置请求体
        httpEntity = new HttpEntity<>(httpHeaders);
    }

}
